Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | /** * API route for listing vision recordings for a player * * GET /api/curriculum/[playerId]/recordings * * Returns list of all vision recordings for the player, sorted by start time (newest first). */ export const dynamic = 'force-dynamic' import { NextResponse } from 'next/server' import { desc, eq } from 'drizzle-orm' import { db } from '@/db' import { visionRecordings } from '@/db/schema' import { withAuth } from '@/lib/auth/withAuth' import { getPlayerAccess, generateAuthorizationError } from '@/lib/classroom' import { getUserId } from '@/lib/viewer' export interface PlayerRecordingItem { id: string sessionId: string status: string durationMs: number | null frameCount: number | null fileSize: number | null startedAt: string endedAt: string | null expiresAt: string videoUrl: string | null } export interface PlayerRecordingsResponse { recordings: PlayerRecordingItem[] totalCount: number } /** * GET - List all recordings for a player */ export const GET = withAuth(async (request, { params }) => { try { const { playerId } = (await params) as { playerId: string } if (!playerId) { return NextResponse.json({ error: 'Player ID required' }, { status: 400 }) } // Authorization check const userId = await getUserId() const access = await getPlayerAccess(userId, playerId) if (access.accessLevel === 'none') { const authError = generateAuthorizationError(access, 'view', { actionDescription: 'view recordings for this student', }) return NextResponse.json(authError, { status: 403 }) } // Parse pagination from query params const url = new URL(request.url) const limit = Math.min(parseInt(url.searchParams.get('limit') || '20', 10), 100) const offset = parseInt(url.searchParams.get('offset') || '0', 10) // Get recordings for this player, sorted by start time const recordings = await db.query.visionRecordings.findMany({ where: eq(visionRecordings.playerId, playerId), orderBy: [desc(visionRecordings.startedAt)], limit, offset, }) // Get total count const allRecordings = await db.query.visionRecordings.findMany({ where: eq(visionRecordings.playerId, playerId), columns: { id: true }, }) const totalCount = allRecordings.length // Transform to response format const result: PlayerRecordingItem[] = recordings.map((recording) => ({ id: recording.id, sessionId: recording.sessionId, status: recording.status, durationMs: recording.durationMs, frameCount: recording.frameCount, fileSize: recording.fileSize, startedAt: recording.startedAt.toISOString(), endedAt: recording.endedAt?.toISOString() ?? null, expiresAt: recording.expiresAt.toISOString(), videoUrl: recording.status === 'ready' ? `/api/curriculum/${playerId}/sessions/${recording.sessionId}/recording/video` : null, })) const response: PlayerRecordingsResponse = { recordings: result, totalCount, } return NextResponse.json(response) } catch (error) { console.error('Error fetching player recordings:', error) return NextResponse.json({ error: 'Failed to fetch recordings' }, { status: 500 }) } }) |